package cn.zhouchao.basic.service.impl;

import cn.zhouchao.basic.constant.BaseConstant;
import cn.zhouchao.basic.dto.EmailRegisterDto;
import cn.zhouchao.basic.dto.VerifyCodeDto;
import cn.zhouchao.basic.service.IVerifyCodeService;
import cn.zhouchao.basic.util.*;
import cn.zhouchao.org.domain.Employee;
import cn.zhouchao.org.mapper.EmployeeMapper;
import cn.zhouchao.user.domain.Logininfo;
import cn.zhouchao.user.domain.User;
import cn.zhouchao.user.mapper.LogininfoMapper;
import cn.zhouchao.user.mapper.UserMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class VerifyCodeServiceImpl implements IVerifyCodeService {
    //外部注入redis=======  注意，请先打开redis服务器
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private LogininfoMapper logininfoMapper;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private EmployeeMapper employeeMapper;
//=================================================================
    /**
     * 获取图片验证码
     * @param imageCodeKey
     * @return
     */
    @Override
    public String verifyCode(String imageCodeKey) {
        //1.非空判断(@PathVariable已经校验了)
        //2.创建一个4位数的验证码
        String code = StrUtils.getComplexRandomString(4);
        //使用redis的对象调用装value的方法，并set自定义的value，以设置参数（redis的key，redis的value，过期时间，时间单位）
        redisTemplate.opsForValue().set(imageCodeKey,code,3, TimeUnit.MINUTES);
        String baser64 = null;
        try { //抛异常
            //设置验证码的 宽 高 内容
            baser64 = VerifyCodeUtils.VerifyCode(115, 40, code);
        } catch (IOException e) {
            e.printStackTrace();
        }
        //3.返回baser64给前端
        return baser64;
    }
//=================================================================
    /**
     * 发送手机短信
     * @param dto
     */
    @Override
    public void smsCode(VerifyCodeDto dto) {
        if ("binder".equals(dto.getType())){
            // 发送短信验证码
            SendMsgCode(dto,BaseConstant.WechatConstant.BUSSINESS_BINDER_PREFIX);
        }else if ("register".equals(dto.getType())){
            /*参数校验:先验证内存中的，再验证数据库中的 */
            // 验证图片验证码
            verifyImageCode(dto);
            // 发送短信验证码
            SendMsgCode(dto,BaseConstant.verifyCodeConstant.BUSSINESS_REGISTER_PREFIX);
        }


    }

    /**
     * 手机号注册
     * @param dto
     */
    @Override
    public void phoneRegister(VerifyCodeDto dto) {
        // 参数校验
        // 验证两次密码是否一致
        if (!dto.getPassword().equals(dto.getPasswordRepeat())){
            throw new BusinessException("两次密码不一致！");
        }
        // 验证手机号验证码是否正确
        String format = String.format(BaseConstant.verifyCodeConstant.BUSSINESS_REGISTER_PREFIX, dto.getPhone());
        String redisPhoneCode = (String) redisTemplate.opsForValue().get(format);
        if (StringUtils.isBlank(redisPhoneCode) || !redisPhoneCode.equals(dto.getPhoneCode())){//
            throw new BusinessException("手机验证码错误！");
        }
        // 验证手机号是否注册过
        User user = userMapper.findByPhone(dto.getPhone());
        if (null != user) {
            throw new BusinessException("该手机号已被注册，请前往登陆界面进行登录！");
        }
        Employee emp = employeeMapper.findByPhone(dto.getPhone());
        // 保存数据 先 logininfo 再user
        user = dto.getUser();
        emp = dto.getEmployee();
        // 构建logininfo对象
        Logininfo logininfo = new Logininfo();
        // 这个api 会将user对象中的字段值 复制到logininfo中 前提是两个实体字段相同
        BeanUtils.copyProperties(user,logininfo);
        BeanUtils.copyProperties(emp,logininfo);
        logininfo.setDisable(true);
        logininfo.setType(1);
        // 添加logininfo表
        logininfoMapper.add(logininfo);
        // 在添加user表 将logininfoid set到user表中
        user.setLogininfoId(logininfo.getId());
        emp.setLogininfoId(logininfo.getId());
        userMapper.add(user);
        employeeMapper.add(emp);

    }
//=================================================================
    /**
     * 发送邮箱消息
     * @param dto
     */
    @Override
    public void emailCode(EmailRegisterDto dto) {
        /*参数校验:先验证内存中的，再验证数据库中的 */
        // 验证图片验证码
        emailImagelverify(dto);
        // 发送邮件验证码
        SendEmailCode(dto);
    }

    /**
     * 邮箱注册
     * @param dto
     */
    @Override
    public void emailRegister(EmailRegisterDto dto) {
        // 参数校验
        // 验证两次密码是否一致
        if (!dto.getPassword().equals(dto.getPasswordRepeat())){
            throw new BusinessException("两次密码不一致！");
        }
        // 验证邮箱验证码是否正确
        String format = String.format(BaseConstant.verifyCodeConstant.BUSSINESS_REGISTER_PREFIX, dto.getEmail());
        String redisPhoneCode = (String) redisTemplate.opsForValue().get(format);
        if (StringUtils.isBlank(redisPhoneCode) || !redisPhoneCode.equals(dto.getEmailCode())){//
            throw new BusinessException("邮箱验证码错误！");
        }
        // 验证邮箱号是否注册过
        User user = userMapper.findByEmail(dto.getEmail());
        if (null != user) {
            throw new BusinessException("该邮箱号已被注册，请前往登陆界面进行登录！");
        }
        Employee emp = employeeMapper.findByEmail(dto.getEmail());
        // 保存数据 先 logininfo 再user
        user = dto.getUser();
        emp = dto.getEmployee();
        // 构建logininfo对象
        Logininfo logininfo = new Logininfo();
        // 这个api 会将user对象中的字段值 复制到logininfo中 前提是两个实体字段相同
        BeanUtils.copyProperties(user,logininfo);
        BeanUtils.copyProperties(emp,logininfo);
        logininfo.setDisable(true);
        logininfo.setType(1);
        // 添加logininfo表
        logininfoMapper.add(logininfo);
        // 在添加user表 将logininfoid set到user表中
        user.setLogininfoId(logininfo.getId());
        emp.setLogininfoId(logininfo.getId());
        userMapper.add(user);
        employeeMapper.add(emp);
    }

//=================================================================
    /*发送邮件验证码*/
    private void SendEmailCode(EmailRegisterDto dto) {
        //先获取redis中手机验证码的key
        String format = String.format(BaseConstant.verifyCodeConstant.BUSSINESS_REGISTER_PREFIX, dto.getEmail());
        //获取邮箱验证码过期时间expirationTime
        Long expirationTime = redisTemplate.getExpire(format);
        //4.判断邮箱号验证码两次发送时间是否大于60秒(5*60L-1*60L=SUBTRACT_RESULT 常量)
        if(expirationTime > BaseConstant.verifyCodeConstant.SUBTRACT_RESULT){
            // 就是一分钟内访问了两次（为了避免客户重复1分钟内多次获取）
            throw new BusinessException("请等等再点！");//抛异常给前端
        }
        //5.判断是否需要重新生成手机号验证码（基于我们目前reids中有手机号验证码 如果有 我们就发送老的 没有我们就生成一个新的）
        String EmailVerifyICode = (String) redisTemplate.opsForValue().get(format);//redis中的手机验证码value
        //判断如果redis中邮箱号验证码 ，没有/过期了，我们就再生成一个
        if (StringUtils.isBlank(EmailVerifyICode)){
            EmailVerifyICode = StrUtils.getRandomString(4);//将生成的验证码再赋值给redis中的空key的value
        }
        log.info("邮箱验证码：{}",EmailVerifyICode);//打印手机验证码
        //6.将邮箱号验证码存到redis(key,value,过期时间，时间类型：秒)
        redisTemplate.opsForValue().set(format,EmailVerifyICode,BaseConstant.verifyCodeConstant.PHONE_CODE_PAST_TIME,TimeUnit.SECONDS);
        //7.发送邮件
        EmailUtil.sendEmailSms(EmailVerifyICode);
    }

    /*判断:邮箱图片验证码正误+邮箱号是否已注册*/
    private void emailImagelverify(EmailRegisterDto dto) {
        //1、判断图片验证码
        String ImageCode = (String) redisTemplate.opsForValue().get(dto.getEmailImageCodeKey());//获取redis中的验证码
        if (StringUtils.isBlank(ImageCode) || !ImageCode.equalsIgnoreCase(dto.getEmailImageCodeValue())){ //是否存在||是否正确
            throw new BusinessException("图片验证码错误！");//抛异常给前端
        }
        //2.判断数据库里卖该邮箱号是否已经存在
        User user = userMapper.findByEmail(dto.getEmail());
        if (null != user){
            throw new BusinessException("该邮箱号已被注册，请前往登陆界面进行登录！");//抛异常给前端
        }
    }

//==================================================================
    /*发送手机短信验证码*/
    private void SendMsgCode(VerifyCodeDto dto,String rediskey) {
        //先获取redis中手机验证码的key
        String format = String.format(rediskey, dto.getPhone());
        //获取手机验证码过期时间expirationTime
        Long expirationTime = redisTemplate.getExpire(format);
        //4.判断手机号验证码两次发送时间是否大于60秒(5*60L-1*60L=SUBTRACT_RESULT 常量)
        if(expirationTime > BaseConstant.verifyCodeConstant.SUBTRACT_RESULT){
            // 就是一分钟内访问了两次（为了避免客户重复1分钟内多次获取）
            throw new BusinessException("请等等再点！");//抛异常给前端
        }
        //5.判断是否需要重新生成手机号验证码（基于我们目前reids中有手机号验证码 如果有 我们就发送老的 没有我们就生成一个新的）
        String phoneVerifyICode = (String) redisTemplate.opsForValue().get(format);//redis中的手机验证码value
        //判断如果redis中手机号验证码 ，没有/过期了，我们就再生成一个
        if (StringUtils.isBlank(phoneVerifyICode)){
            phoneVerifyICode = StrUtils.getRandomString(4);//将生成的验证码再赋值给redis中的空key的value
        }
        log.info("手机验证码：{}",phoneVerifyICode);//打印手机验证码
        //6.将手机号验证码存到redis(key,value,过期时间，时间类型：秒)
        redisTemplate.opsForValue().set(format,phoneVerifyICode,BaseConstant.verifyCodeConstant.PHONE_CODE_PAST_TIME,TimeUnit.SECONDS);
        //7.发送短信
        // SendMsgUtil.sendPhoneSms();
    }

    /*判断:手机图片验证码正误+手机号是否已注册*/
    private void verifyImageCode(VerifyCodeDto dto) {
        //1、判断图片验证码
        String ImageCode = (String) redisTemplate.opsForValue().get(dto.getPhoneImageCodeKey());//获取redis中的验证码
        if (StringUtils.isBlank(ImageCode) || !ImageCode.equalsIgnoreCase(dto.getPhoneImageCodeValue())){ //是否存在||是否正确
            throw new BusinessException("图片验证码错误！");//抛异常给前端
        }
        //2.判断数据库里卖该手机号是否已经存在
        User user = userMapper.findByPhone(dto.getPhone());
        if (null != user){
            throw new BusinessException("该手机号已被注册，请前往登陆界面进行登录！");//抛异常给前端
        }
    }

}
